/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.codesourcery.jasm16.ide.ui.views;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import org.apache.commons.lang.StringUtils;
import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.Size;
import de.codesourcery.jasm16.emulator.EmulationListener;
import de.codesourcery.jasm16.emulator.ICPU;
import de.codesourcery.jasm16.emulator.IEmulationListener;
import de.codesourcery.jasm16.emulator.IEmulator;
import de.codesourcery.jasm16.emulator.IReadOnlyCPU;
import de.codesourcery.jasm16.emulator.memory.MemUtils;
import de.codesourcery.jasm16.ide.ui.utils.UIUtils;
import de.codesourcery.jasm16.utils.ITextRegion;
import de.codesourcery.jasm16.utils.Misc;
import de.codesourcery.jasm16.utils.TextRegion;
public class CPUView extends AbstractView
{
public static final String VIEW_ID = "cpu-view";
private JPanel panel;
@SuppressWarnings("unused")
private final SimpleAttributeSet defaultStyle;
private final SimpleAttributeSet errorStyle;
private final JTextPane textArea = new JTextPane();
private volatile IEmulator emulator;
private final IEmulationListener listener = new EmulationListener() {
@Override
public void afterCommandExecution(IEmulator emulator, int commandDuration)
{
if ( ! isFullSpeedMode() ) {
refreshDisplay();
}
}
@Override
public void afterReset(IEmulator emulator)
{
if ( ! isFullSpeedMode() ) {
refreshDisplay();
}
}
@Override
public void afterMemoryLoad(IEmulator emulator, Address startAddress, int lengthInBytes)
{
if ( ! isFullSpeedMode() ) {
refreshDisplay();
}
}
@Override
public void onStopHook(IEmulator emulator, Address previousPC, Throwable emulationError) {
refreshDisplay();
}
};
public CPUView()
{
errorStyle = createStyle(Color.RED);
defaultStyle = createStyle(Color.GREEN);
}
public CPUView(IEmulator emulator)
{
this();
setEmulator( emulator );
}
protected final static SimpleAttributeSet createStyle(Color color)
{
SimpleAttributeSet result = new SimpleAttributeSet();
StyleConstants.setForeground( result , color );
return result;
}
@Override
public void refreshDisplay()
{
UIUtils.invokeLater( new Runnable() {
@Override
public void run()
{
internalRefreshDisplay();
}
});
}
private void internalRefreshDisplay()
{
if ( emulator == null ) {
return;
}
final IReadOnlyCPU cpu = emulator.getCPU();
final StringBuilder builder = new StringBuilder();
final List<ITextRegion> redRegions = new ArrayList<ITextRegion>();
Throwable lastError = emulator.getLastEmulationError();
if ( lastError != null )
{
final String msg = StringUtils.isBlank( lastError.getMessage() ) ? lastError.getClass().getName() : lastError.getMessage();
builder.append("Emulation stopped with an error: "+msg+"\n");
redRegions.add( new TextRegion( 0 , builder.length() ) );
}
int itemsInLine = 0;
for ( int i = 0 ; i < ICPU.COMMON_REGISTER_NAMES.length ; i++ )
{
final int value = cpu.getRegisterValue( ICPU.COMMON_REGISTERS[i] );
builder.append( ICPU.COMMON_REGISTER_NAMES[i]+": "+Misc.toHexString( value )+" ");
Address address = Address.wordAddress( value );
final byte[] data = MemUtils.getBytes( emulator.getMemory() ,
address ,
Size.words( 4 ) ,
true
);
builder.append( Misc.toHexDump( address , data , data.length , 4, true , false, true ) );
builder.append("\n");
itemsInLine++;
if ( itemsInLine == 4 ) {
itemsInLine = 0;
builder.append("\n");
}
}
builder.append("\nPC: "+Misc.toHexString( cpu.getPC().getValue() ) );
builder.append(" (elapsed cycles: "+cpu.getCurrentCycleCount()).append(")");
builder.append("\n");
builder.append("EX: "+Misc.toHexString( cpu.getEX() )).append("\n");
builder.append("IA: "+Misc.toHexString( cpu.getInterruptAddress() )).append("\n");
builder.append("IQ: Interrupt queueing is ");
if ( cpu.isQueueInterrupts() )
{
int start = builder.length();
builder.append("ON");
redRegions.add( new TextRegion( start , builder.length() - start ) );
} else {
builder.append("OFF");
}
builder.append("\n");
builder.append("IRQs: "+StringUtils.join( cpu.getInterruptQueue() , "," )).append("\n");
builder.append("SP: "+Misc.toHexString( cpu.getSP().getValue() )).append("\n");
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run()
{
final StyledDocument doc = textArea.getStyledDocument();
doc.putProperty(Document.StreamDescriptionProperty, null);
textArea.setText( builder.toString() );
for ( ITextRegion region : redRegions ) {
doc.setCharacterAttributes( region.getStartingOffset() , region.getLength() , errorStyle , true );
}
}
});
}
public void setEmulator(IEmulator emulator)
{
if (emulator == null) {
throw new IllegalArgumentException("emulator must not be NULL.");
}
if ( this.emulator == emulator ) {
return;
}
if ( this.emulator != null ) {
this.emulator.removeEmulationListener( listener );
}
this.emulator = emulator;
emulator.addEmulationListener( listener );
}
@Override
public void disposeHook()
{
if ( this.emulator != null ) {
this.emulator.removeEmulationListener( listener );
this.emulator = null;
}
}
protected JPanel createPanel()
{
textArea.setFont( getMonospacedFont() );
textArea.setEditable( false );
setColors( textArea );
final JPanel panel = new JPanel();
setColors( panel );
panel.setLayout( new GridBagLayout() );
final GridBagConstraints cnstrs = constraints( 0 , 0 , true , true , GridBagConstraints.BOTH );
panel.add( textArea , cnstrs );
return panel;
}
@Override
public JPanel getPanel()
{
if ( panel == null ) {
panel = createPanel();
}
return panel;
}
@Override
public String getTitle() {
return "CPU view";
}
@Override
public String getID() {
return VIEW_ID;
}
}